#include "fork.h"
#include "string.h"
#include "global.h"
#include "thread.h"
#include "debug.h"
#include "process.h"
#include "file.h"	//MAX_FILE_OPEN defined here
#include "memory.h"	//PF_KERNEL=1 defined here
#include "interrupt.h"	//INTR_OFF
extern void intr_exit(void);	//在kernel.S中定义，今番引用过来

/* 将父进程的pcb拷贝给子进程 */
static int32_t copy_pcb_vaddrbitmap_stack0(struct task_struct* child_thread, struct task_struct* parent_thread) {
	/* a 复制pcb所在的整个页，里面包含进程pcb信息及特权级0级的栈，里面包含了返回地址 */
	memcpy(child_thread, parent_thread, PG_SIZE);
	child_thread->pid = fork_pid();
	child_thread->elapsed_ticks = 0;
	child_thread->status = TASK_READY;
	child_thread->ticks = child_thread->priority;	//为新进程把时间片充满
	child_thread->parent_pid = parent_thread->pid;
	child_thread->general_tag.prev = child_thread->general_tag.next = NULL;
	child_thread->all_list_tag.prev = child_thread->all_list_tag.next = NULL;
	block_desc_init(child_thread->u_block_desc);
	/* 复制父进程的虚拟地址池位图 */
	uint32_t bitmap_pg_cnt = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8, PG_SIZE);
	void* vaddr_btmp = get_kernel_pages(bitmap_pg_cnt);	//准备内存
	/* 此时child_thread->userprog_vaddr.vaddr_bitmap.bit还是指向父进程虚拟地址的位图地址
	 * 下面将child_thread->userprog_vaddr.vaddr_bitmap.bit
	 * 指向自己的位图vaddr_btmp*/
	memcpy(vaddr_btmp, child_thread->userprog_vaddr.vaddr_bitmap.bits, bitmap_pg_cnt*PG_SIZE);	//复制数据
	child_thread->userprog_vaddr.vaddr_bitmap.bits = vaddr_btmp;
	/* 调试用 */
	ASSERT(strlen(child_thread->name) < 11);
	strcat(child_thread->name, "_fork");
	return 0;
}

/* 复制子进程的进程体（代码和数据）及用户栈 */
static void copy_body_stack3(struct task_struct* child_thread, struct task_struct* parent_thread, void* buf_page) {
	uint8_t* vaddr_btmp = parent_thread->userprog_vaddr.vaddr_bitmap.bits;
	uint32_t btmp_bytes_len = parent_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len;
	uint32_t vaddr_start = parent_thread->userprog_vaddr.vaddr_start;
	uint32_t idx_byte = 0;
	uint32_t idx_bit = 0;
	uint32_t prog_vaddr = 0;

	/* 在父进程的用户空间查找已有的数据页 */
	while (idx_byte < btmp_bytes_len) {
		if (vaddr_btmp[idx_byte]) {
			idx_bit = 0;
			while (idx_bit < 8) {
				if ((BITMAP_MASK << idx_bit) & vaddr_btmp[idx_byte]) {
					prog_vaddr = (idx_byte*8 + idx_bit)*PG_SIZE + vaddr_start;
					/* 下面的操作是将父进程用户空间的数据通过内核空间做中转，
					 * 最终复制到子进程的用户空间*/

					/* a 将父进程在用户空间的数据复制到内核缓冲区buf_page，
					 * 目的是下面切换到子进程的页表后，还能访问到父进程的数据*/
					memcpy(buf_page, (void*)prog_vaddr, PG_SIZE);

					/* b 将页表切换到子进程，目的是避免下面申请内存的函数将pte及pde安装到父进程的页表中 */
					page_dir_activate(child_thread);
					/* c 申请虚拟地址prog_vaddr */
					get_a_page_without_opvaddrbitmap(PF_USER, prog_vaddr);

					/* d 从内核缓冲区中将父进程数据复制到子进程的用户空间 */
					memcpy((void*)prog_vaddr, buf_page, PG_SIZE);

					/* e 恢复父进程页表 */
					page_dir_activate(parent_thread);
				}
				idx_bit++;
			}
		}
		idx_byte++;
	}
}

/* 为子进程构建thread_stack和修改返回值 */
static int32_t build_child_stack(struct task_struct* child_thread) {
	/* a 使子进程pid返回值为0 */
	/* 获取子进程0级栈栈顶 */
	struct intr_stack* intr_0_stack = (struct intr_stack*)\
	((uint32_t)child_thread + PG_SIZE - sizeof(struct intr_stack));
	/* 修改子进程的返回值为0 */
	intr_0_stack->eax = 0;

	/* b 为switch to 构建struct thread_stack,
	将其构建在紧邻intr_stack之下的空间 */
	uint32_t* ret_addr_in_thread_stack = (uint32_t*)intr_0_stack - 1;

	/* 这三行是不必要的，只是为了梳理thread_stack中的关系 */
	uint32_t* esi_ptr_in_thread_stack = (uint32_t*)intr_0_stack - 2;
	uint32_t* edi_ptr_in_thread_stack = (uint32_t*)intr_0_stack - 3;
	uint32_t* ebx_ptr_in_thread_stack = (uint32_t*)intr_0_stack - 4;


	/* ebp在thread_stack中的地址便是当时的esp（0级栈的栈顶），
	即esp为 “(uint32_t)intr_0_stack - 5” */
	uint32_t* ebp_ptr_in_thread_stack = (uint32_t*)intr_0_stack - 5;

	/* switch_to的返回地址更新为intr_exit，直接从中断返回 */
	*ret_addr_in_thread_stack = (uint32_t)intr_exit;

	/* 下面这两行赋值只是为了使构建的thread_stack更加清晰，
	其实也不需要，因为在进入intr_exit后一系列的pop会把寄存器中的数据覆盖 */
	*ebp_ptr_in_thread_stack = *ebx_ptr_in_thread_stack =\
	*edi_ptr_in_thread_stack = *esi_ptr_in_thread_stack = 0;


	/* 把构建的thread_stack的栈作为switch_to恢复数据时的栈顶 */
	child_thread->self_kstack = ebp_ptr_in_thread_stack;
	return 0;
}

/* 更新inode打开数 */
static void update_inode_open_cnt(struct task_struct* thread) {
	int32_t local_fd = 3, global_fd = 0;
	while(local_fd < MAX_FILES_OPEN_PER_PROC) {
		global_fd = thread->fd_table[local_fd];
		ASSERT(global_fd < MAX_FILE_OPEN);
		if (global_fd != -1) {
			file_table[global_fd].fd_inode->i_open_cnts++;
		}
		local_fd++;
	}
}

/* 拷贝父进程本身所占资源给子进程 */
static int32_t copy_process(struct task_struct* child_thread, struct task_struct* parent_thread) {
	/* 内核缓冲区，作为父进程用户空间的数据复制到子进程用户空间的中转 */
	void* buf_page = get_kernel_pages(1);
	if (buf_page == NULL) {
		return -1;
	}

	/* a 复制父进程的pcb、虚拟地址位图、内核栈到子进程 */
	if (copy_pcb_vaddrbitmap_stack0(child_thread, parent_thread) == -1) {
		return -1;
	}

	/* b 为子进程创建页表，此页表仅包括内核空间 */
	child_thread->pgdir = create_page_dir();
	if (child_thread->pgdir == NULL) {
		return -1;
	}

	/* c 复制父进程进程体即用户栈给子进程 */
	copy_body_stack3(child_thread, parent_thread, buf_page);

	/* d 构建子进程thread_stack和修改返回值pid */
	build_child_stack(child_thread);

	/* e 更新文件inode的打开数 */
	update_inode_open_cnts(child_thread);

	mfree_page(PF_KERNEL, buf_page, 1);
	return 0;
}

/* fork子进程，内核线程不可直接调用 */
pid_t sys_fork(void) {
	struct task_struct* parent_thread = running_thread();
	struct task_struct* child_thread = get_kernel_pages(1);	//为子进程创建pcb（task_struct结构）
	if (child_thread == NULL) {
		return -1;
	}
	ASSERT(INTR_OFF == intr_get_status() && parent_thread->pgdir != NULL)

	if (copy_process(child_thread, parent_thread) == -1) {
		return -1;
	}

	/* 添加到就绪线程队列和所有线程队列，子进程由调试器安排运行 */
	ASSERT(!elem_find(&thread_ready_list, &child_thread->general_tag));
	list_append(&thread_ready_list, &child_thread->general_tag);
	ASSERT(!elem_find(thread_all_list, &child_thread->all_list_tag));
	list_append(&thread_all_list, &child_thread->all_list_tag);

	return child_thread->pid;	//父进程返回子进程的pid，子进程返回pid=0
}